using System;
using OhioState.Math;

namespace OhioState.Graphics.Proxy
{
    /// <summary>
    /// Boundingbox class 
    /// </summary>
    [Serializable()]
    public class BoundingBox : IBoundingVolume, ICloneable
    {
        /// <summary>
        /// Boundingbox constructor
        /// </summary>
        public BoundingBox()
            : this(new Vector3(), new Vector3(1,1,1))
        {
        }
        /// <summary>
        /// BoundingBox constructor given the dimensions
        /// </summary>
        /// <param name="lowerLeft">lower left vertex</param>
        /// <param name="width">width</param>
        /// <param name="height">height</param>
        /// <param name="depth">depth</param>
        public BoundingBox(Vector3 lowerLeft, float width, float height, float depth)
        {
            this._lowerLeft = new Vector3(lowerLeft);
            this.Width = width;
            this.Height = height;
            this.Depth = depth;
        }

        /// <summary>
        /// BoundingBox constructor given the lowerleft and upper right vertices
        /// </summary>
        /// <param name="lowerLeft">lower left vertex</param>
        /// <param name="upperRight">upper right vertex</param>
        public BoundingBox(Vector3 lowerLeft, Vector3 upperRight)
        {
            this._lowerLeft = new Vector3(lowerLeft);
            this.UpperRight = upperRight;
        }
        /// <summary>
        /// BoundingBox constructor; given a box
        /// </summary>
        /// <param name="box">bounding box</param>
        public BoundingBox(BoundingBox box)
        {
            this._lowerLeft = new Vector3(box.LowerLeft);
            this.UpperRight = box.UpperRight;
        }

        private Vector3 _lowerLeft;
        /// <summary>
        /// Gets or sets the lower left vertex of the bounding box
        /// </summary>
        public Vector3 LowerLeft
        {
            get { return this._lowerLeft; }
            set
            {
                this._lowerLeft = new Vector3(value);
            }
        }
        /// <summary>
        /// Gets or sets teh upper right vertex of the bounding box
        /// </summary>
        public Vector3 UpperRight
        {
            get { return new Vector3(_lowerLeft[0]+_width,_lowerLeft[1]+_height,_lowerLeft[2]+_depth); }
            set
            {
                this.Width = value[0] - _lowerLeft[0];
                this.Height = value[1] - _lowerLeft[1];
                this.Depth = value[2] - _lowerLeft[2];
            }
        }
        private float _width;
        
        /// <summary>
        /// Get or set the Width of the box
        /// </summary>
        public float Width
        {
            get { return _width; }
            set
            {
                if (value >= 0.0f)
                {
                    _width = value;
                }
            }
        }
        private float _height;
        /// <summary>
        /// Get/Set the Height of the box
        /// </summary>
        public float Height
        {
            get { return _height; }
            set
            {
                if (value >= 0.0f)
                {
                    _height = value;
                }
            }
        }
        private float _depth;
        /// <summary>
        /// Get/Set the Depth of the box
        /// </summary>
        public float Depth
        {
            get { return _depth; }
            set
            {
                if (value >= 0.0f)
                {
                    _depth = value;
                }
            }
        }
        /// <summary>
        /// Clone method; makes a clone of 'this' object
        /// </summary>
        /// <returns></returns>
        public Object Clone()
        {
            BoundingBox bBox = new BoundingBox(_lowerLeft, _width, _height, _depth);
            return bBox;
        }
        /// <summary>
        /// Overrides shared object ToString method
        /// </summary>
        /// <returns>A string representing parent shared object name</returns>
        public override string ToString()
        {
            return "Not implemented yet.";
        }

        #region IBoundingVolume interface
        /// <summary>
        /// Get the centroid of the box
        /// </summary>
        public Vector3 Centroid
        {
            get
            {
                Vector3 center = new Vector3(_width, _height, _depth);
                center = _lowerLeft + 0.5f * center;
                return center;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public bool ContainsPoint(Vector3 point)
        {
            bool inside = true;
            inside = inside | (point[0] >= _lowerLeft[0]);
            inside = inside | (point[1] >= _lowerLeft[1]);
            inside = inside | (point[2] >= _lowerLeft[2]);
            inside = inside | (point[0] <= _lowerLeft[0]+_width);
            inside = inside | (point[1] <= _lowerLeft[1]+_height);
            inside = inside | (point[2] <= _lowerLeft[2]+_depth);
            return inside;
        }
        /// <summary>
        /// Implementation of IBoundingVolume ClosestPoint method
        /// </summary>
        /// <param name="target">target point</param>
        /// <returns>closest point</returns>
        public Vector3 ClosestPoint(Vector3 target)
        {
            Vector3 point = new Vector3();
            Vector3 tmp1 = (target - _lowerLeft);
            tmp1.absoluteValue();
            Vector3 tmp2 = (target - new Vector3(_width,_height,_depth));
            tmp2.absoluteValue();
            point[0] = (tmp1[0] < tmp2[0]) ? _lowerLeft[0] : _lowerLeft[0]+_width;
            point[1] = (tmp1[1] < tmp2[1]) ? _lowerLeft[1] : _lowerLeft[1]+_height;
            point[2] = (tmp1[2] < tmp2[2]) ? _lowerLeft[2] : _lowerLeft[2]+_depth;
            return point;
        }
        #endregion IBoundingVolume interface
    }
}
